
package com.tools.cleanmaster.controller;

import android.content.Context;
import android.content.pm.PackageManager.NameNotFoundException;
import android.text.TextUtils;

import com.tools.cleanmaster.appinfo.AppInfoCompat;
import com.tools.cleanmaster.appinfo.AppManagerCompat;
import com.tools.cleanmaster.model.TrashType;
import com.tools.cleanmaster.model.cloud.TrashPostContant;
import com.tools.cleanmaster.model.cloud.TrashQueryPathManager;
import com.tools.cleanmaster.model.cloud.TrashUnknowPathData;
import com.tools.cleanmaster.model.db.AllTrashBean;
import com.tools.cleanmaster.model.db.CacheTrashBean;
import com.tools.cleanmaster.model.db.DBBeanFilter;
import com.tools.cleanmaster.model.db.Decode;
import com.tools.cleanmaster.model.db.RegularTrashBean;
import com.tools.cleanmaster.model.db.ResidualTrashBean;
import com.tools.cleanmaster.model.db.TrashDB;
import com.tools.cleanmaster.model.db.TrashDBBeanFilter;
import com.tools.cleanmaster.model.item.AppTrashItem;
import com.tools.cleanmaster.model.item.FilePathBean;
import com.tools.cleanmaster.model.item.PathUpdateInfo;
import com.tools.cleanmaster.model.item.RegularTrashData;
import com.tools.cleanmaster.model.item.TrashItem;
import com.tools.cleanmaster.store.TrashCloudSharedPrefManager;
import com.tools.cleanmaster.utils.FeatureConfig;
import com.tools.cleanmaster.utils.FileUtils;
import com.tools.cleanmaster.utils.LogHelper;
import com.tools.cleanmaster.utils.OptimizerFile;
import com.tools.cleanmaster.utils.TrashUtils;
import com.tools.cleanmaster.utils.TraversalSDPathTask;

import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;

public class TraversalCacheAndResidualScanner extends TrashCacheAndResidualScanner {

    private static final String SUB_TAG = "TraversalScanner";

    private static final String TAG = SUB_TAG + "." + TrashPostContant.TAG;

    public static final String TAG_DB = SUB_TAG + "." + "TrashDB";

    private static final String TAG_TIME = SUB_TAG + ".Time";

    private static final String ANDROID_DATA = "/android/data/";

    public static final int MAX_DEPTH = 3; // 层级从1开始

    public static final int START_DEPTH = 0;

    private final String[] mRootDirs;

    private boolean mTravesalFinished = false;

    private List<FilePathBean> mSafeListInDB;

    private List<FilePathBean> mUnknowListInDB;

    private final List<String> mUnknowPathList = new ArrayList<String>();

    private final List<String> mCloudQueryList = new ArrayList<String>();

    private HashMap<String, String> mDecodedPathsMap;

    private Map<String, String> mDecodedPkgsMap = new HashMap<String, String>();

    private List<RegularTrashBean> mAllRegulars;

    private long mCacheFileSize = 0;

    private long mResidualSize = 0;

    private final int mQueryType;

    private Map<File, List<RegularTrashData>> mAllRegularTrashBean = new HashMap<File, List<RegularTrashData>>();

    private DBBeanFilter<ResidualTrashBean> mDBFilter;

    private List<ResidualTrashBean> mNativeHitResidual = new ArrayList<ResidualTrashBean>();

    private List<CacheTrashBean> mNativeHitCache = new ArrayList<CacheTrashBean>();

    public TraversalCacheAndResidualScanner(Context context, TrashHandler handler, int queryType) {
        super(context, handler);
        mRootDirs = handler.getRootDirs();
        mQueryType = queryType;
    }

    @Override
    public void scan(int increaseProgress) {
        if (mIsScanning) {
            return;
        }
        mIsScanning = true;

        if (mStopRequested) {
            return;
        }
        long nowTime = System.currentTimeMillis();

        if (nowTime - TrashCloudSharedPrefManager.getUnkonwnCleanTime() >= TimeUnit.DAYS.toMillis(7)) {
            TrashCloudSharedPrefManager.setUnkonwnClean(nowTime);
            TrashDB.getInstance().setUnknownPathInvalid();
        }

        if (FeatureConfig.DEBUG_LOG) {
            LogHelper.i(SUB_TAG, "开始递归扫描，进度＝" + increaseProgress);
        }
        // 1.获取SD卡所有1级路径
        List<String> dirs = getRootPathList();
        if (FeatureConfig.DEBUG_LOG) {
            for (String string : dirs) {
                LogHelper.i(SUB_TAG, "一级路径＝" + string);
            }
        }
        long startTIme = System.currentTimeMillis();
        // 2.根据1级路径去垃圾库查询数据
        Map<String, AllTrashBean> subMap = querySubDirs(dirs);
        userTimeLog("DB", startTIme);

        int progressDB = increaseProgress / 8;
        if (FeatureConfig.DEBUG_LOG) {
            LogHelper.i(SUB_TAG, "DB进度＝" + progressDB);
        }
        mTrashHandler.updateProgress(progressDB, null);
        if (mStopRequested) {
            return;
        }
        // 3.开始递归遍历
        startTraversal(subMap, increaseProgress - progressDB);
        if (mTravesalFinished) {
            mIsScanning = false;
        }
    }

    private void userTimeLog(String s, long startTIme) {
        if (FeatureConfig.DEBUG_LOG) {
            LogHelper.i(TAG_TIME, s + "耗时＝" + (System.currentTimeMillis() - startTIme));
        }
    }

    /**
     * 是否扫描完成
     *
     * @return
     */
    public boolean isScanFinished() {
        return mTravesalFinished;
    }

    private void startTraversal(Map<String, AllTrashBean> subMap, int progress) {
        if (subMap == null) {
            scanFinish(progress);
            return;
        }
        long startTIme = System.currentTimeMillis();
        int trashProgress = progress / 4;
        if (FeatureConfig.DEBUG_LOG) {
            LogHelper.i(SUB_TAG, "过滤缓存残留进度＝" + trashProgress);
        }
        mTrashHandler.updateProgress(trashProgress, null);

        //对命中的缓存和残留数据的过滤
        for (Entry<String, AllTrashBean> entry : subMap.entrySet()) {
            for (String root : mRootDirs) {
                File file = new File(root, entry.getKey());
                if (file.exists()) {
                    listTraversal(START_DEPTH, MAX_DEPTH, entry.getKey(), entry.getValue(), root);
                    break;
                }
            }
        }

        if (FeatureConfig.DEBUG_LOG) {
            for (String partDirItem : mUnknowPathList) {
                LogHelper.i(SUB_TAG, "递归完成后，未知路径＝" + partDirItem);
            }
        }
        userTimeLog("sd卡递归", startTIme);
        startTIme = System.currentTimeMillis();

        int regularScanProgress = progress / 4;
        if (FeatureConfig.DEBUG_LOG) {
            LogHelper.i(SUB_TAG, "正则进度＝" + regularScanProgress);
        }
        // 4.正则扫描
        doRegularScan(regularScanProgress);
        userTimeLog("正则递归", startTIme);
        if (FeatureConfig.DEBUG_LOG) {
            for (String strItem : mCloudQueryList) {
                LogHelper.i(SUB_TAG, "正则完成后，未知路径＝" + strItem);
            }
        }
        // 5.云端请求START_DEPTH, MAX_DEPTH, entry.getKey
        int cloudProgress = progress - regularScanProgress - trashProgress;
        if (FeatureConfig.DEBUG_LOG) {
            LogHelper.i(TAG, "云端进度＝" + cloudProgress);
        }
        cloudRequest(mCloudQueryList, cloudProgress);
        mTrashHandler.updateProgress(cloudProgress, null);

    }

    private void scanFinish(int progress) {
        mTrashHandler.updateProgress(progress, null);
        mTrashHandler.onTrashTypeFinish(TrashType.APP_TRASH_FILE, 0);
        mTrashHandler.onTrashTypeFinish(TrashType.UNINSTALLED_APP, 0);
        mTravesalFinished = true;
        mIsScanning = false;
        //设置已经云扫过
        TrashCloudSharedPrefManager.setCloudyScanReport(true);
    }

    private void cloudRequest(List<String> mCloudQueryList, final int progress) {
        if (mCloudQueryList == null || mCloudQueryList.size() == 0) {
            scanFinish(progress);
            return;
        }
        handleDirsAndFillUploadData(mCloudQueryList);
        final long startTIme = System.currentTimeMillis();

        TrashQueryPathManager.QueryUnknowPathCallback queryUnknowPathCallback =
                new TrashQueryPathManager.QueryUnknowPathCallback() {
                    @Override
                    public void onQueryStart() {

                    }

                    @Override
                    public void onNetWorkError(int reponseCode) {

                    }

                    @Override
                    public void onQueryComplete() {
                        mTrashHandler.onTrashTypeFinish(TrashType.APP_TRASH_FILE, mCacheFileSize);
                        mTrashHandler.onTrashTypeFinish(TrashType.UNINSTALLED_APP, mResidualSize);
                        mTravesalFinished = true;
                        mIsScanning = false;
                        userTimeLog("云端全部", startTIme);
                        //设置已经云扫过
                        TrashCloudSharedPrefManager.setCloudyScanReport(true);
                    }

                    @Override
                    public void onQueryPartComplete(List<String> pathsList, List<PathUpdateInfo> infos) {
                        //6.云端请求完本地命中数据耗时
                        handleCloudUpdatePath(infos, progress);
                    }
                };
        TrashQueryPathManager.getInstance().startQueryUnknowPath(queryUnknowPathCallback);
    }

    /**
     * @param updates
     */
    private void handleCloudUpdatePath(List<PathUpdateInfo> updates, int progress) {

        if (updates == null || updates.isEmpty()) {
            scan(progress);
            return;
        }

        for (PathUpdateInfo pathUpdateInfo : updates) {
            if (FeatureConfig.DEBUG_LOG) {
                LogHelper.i(TAG, "云端下发数据   pkg=" + pathUpdateInfo);
            }
        }

        List<CacheTrashBean> cacheBeans = new ArrayList<CacheTrashBean>();
        List<ResidualTrashBean> residualBeans = new ArrayList<ResidualTrashBean>();

        for (PathUpdateInfo info : updates) {
            List<PathUpdateInfo.DescriptionPackage> pkgs = info.pkgs;
            if (pkgs == null || pkgs.isEmpty()) {
                continue;
            }
            if (isPathNotExist(info)) {
                continue;
            }

            long fileId = 0;
            try {
                fileId = Long.valueOf(info.pathId);
            } catch (NumberFormatException e) {
                continue;
            }

            for (PathUpdateInfo.DescriptionPackage pkg : pkgs) {
                String packageName = Decode.encode(pkg.mPkg);
                if (info.inst_sugg != -1) {// cache
                    boolean flag = false;
                    for (CacheTrashBean cache : mNativeHitCache) {
                        if (cache.filePath.equalsIgnoreCase(info.path)) {
                            flag = true;
                            break;
                        }
                    }
                    if (!flag) {
                        if (info.inst_sugg != 0) {
                            CacheTrashBean cacheBean = new CacheTrashBean();
                            cacheBean.fileId = fileId;
                            cacheBean.alertInfo = info.inst_desc;
                            cacheBean.pkgName = packageName;
                            cacheBean.deleteLevel = info.inst_sugg;
                            cacheBean.itemName = info.desc;
                            cacheBean.filePath = info.path;
                            cacheBean.lid = info.lid;
                            cacheBeans.add(cacheBean);
                        }
                    }
                }
                if (info.unin_sugg != -1) {// residual
                    //1.本地已经命中过，就没必要继续遍历
                    boolean nativeHit = false;
                    boolean sameFiled = false;
                    for (ResidualTrashBean residual : mNativeHitResidual) {
                        if (residual.filePath.equalsIgnoreCase(info.path)) {
                            nativeHit = true;
                            break;
                        }
                    }
                    if (nativeHit) {
                        continue;
                    }

                    //2.如果fileId一样的，pkgNames相加即可
                    for (ResidualTrashBean bean : residualBeans) {
                        if (bean.fileId == fileId) {
                            if (!TextUtils.isEmpty(bean.pkgNames)) {
                                bean.pkgNames += "+" + packageName;
                            }
                            sameFiled = true;
                            break;
                        }
                    }
                    if (sameFiled) {
                        continue;
                    }
                    if (info.inst_sugg != 0) {
                        ResidualTrashBean residualBean = new ResidualTrashBean();
                        residualBean.fileId = fileId;
                        residualBean.alertInfo = info.unin_desc;
                        residualBean.pkgNames = packageName;
                        residualBean.deleteLevel = info.unin_sugg;
                        residualBean.softName = pkg.mAppName;
                        residualBean.filePath = info.path;
                        residualBean.lid = info.lid;
                        residualBeans.add(residualBean);
                    }
                }
            }
        }
        for (CacheTrashBean bean : cacheBeans) {
            for (String root : mRootDirs) {
                File file = new File(root + bean.filePath);
                if (file.exists()) {
                    onFoundTrash(bean, file, false);
                }
            }
        }
        for (ResidualTrashBean bean : residualBeans) {
            for (String root : mRootDirs) {
                File file = new File(root + bean.filePath);
                if (file.exists()) {
                    onFoundTrash(bean, file, false);
                }
            }
        }
    }

    private boolean isPathNotExist(PathUpdateInfo info) {
        boolean pathNotExist = false;
        for (String root : mRootDirs) {
            if (!new File(root + info.path).exists()) {
                pathNotExist = true;
                break;
            }
        }
        return pathNotExist;
    }

    /**
     * 处理下要上报的数据，并且填充到
     * <p>
     * TrashUnknowPathData
     * </p>
     * 请求队列里
     *
     * @param dirs
     */
    private static void handleDirsAndFillUploadData(List<String> dirs) {
        for (String path : dirs) {
            if (TextUtils.isEmpty(path)) {
                continue;
            }
            if (FeatureConfig.DEBUG_LOG) {
                LogHelper.i(SUB_TAG, "需要云端请求未知路径＝" + path);
            }
            if (path.startsWith(ANDROID_DATA)) {
                String[] paths = path.substring(ANDROID_DATA.length()).split(File.separator);
                String[] pathArray = new String[2];
                if (paths.length < 2) {
                    for (int i = 0; i < paths.length; i++) {
                        pathArray[i] = paths[i];
                    }
                } else {
                    pathArray = paths;
                }
                TrashUnknowPathData.getInstance().addSpecialUnknowPath(pathArray[0], pathArray[1]);
            } else {
                path = FileUtils.removeFrontSlash(path);
                String[] paths = path.split(File.separator);
                String[] addPaths = new String[3];
                if (paths.length < 3) {
                    for (int i = 0; i < paths.length; i++) {
                        addPaths[i] = paths[i];
                    }
                } else {
                    addPaths = paths;
                }
                TrashUnknowPathData.getInstance().addNomalUnknowPath(addPaths[0], addPaths[1], addPaths[2]);
            }
        }
    }

    /**
     * @param depth
     * @param maxDepth
     * @param dirPath  全路径不带sdcard根的(保证全小写)
     * @param trash
     * @param root     根目录
     */
    private void listTraversal(int depth, int maxDepth, String dirPath, AllTrashBean trash,
                               String root) {
        int currentDeep = depth + 1;
        //  android/data的数据最大层数多加1级
        if (FileUtils.removeSlash(ANDROID_DATA).equalsIgnoreCase(dirPath)) {
            maxDepth++;
        }
        boolean subFileNotHasDir = checkFileHasDir(new File(root + dirPath));
        if (subFileNotHasDir && isInServerRequestWhiteList(dirPath)) {

        } else {
            //若不在保护名单和未知名单内,则继续递归
            if (!isInSafeList(dirPath) && !isInUnknowList(dirPath)) {
                if (currentDeep < maxDepth) {
                    //如果没到最后一级，但是该目录没有子目录了,也将其算成最后一级
                    boolean lastDepth = false;
                    if (subFileNotHasDir) {
                        lastDepth = true;
                    }
                    matchTrash(lastDepth, maxDepth, dirPath, trash, root, currentDeep);
                } else {
                    matchTrash(true, maxDepth, dirPath, trash, root, currentDeep);
                }
            }
        }
    }

    /**
     * 判断该路径下面是否有子目录
     */
    private boolean checkFileHasDir(File file) {
        //c实现
        String[] dirs = TrashUtils.listDir(file);
        return dirs == null || dirs.length == 0;
        //java实现
        //        boolean subFileNotHasDir=true;
        //        File[] subFiles = file.listFiles();
        //        if (subFiles != null) {
        //            for (File subFile : subFiles) {
        //                if (subFile.isDirectory()) {
        //                    subFileNotHasDir = false;
        //                    break;
        //                }
        //            }
        //        }
        //        return subFileNotHasDir;
    }

    private boolean isInServerRequestWhiteList(String path) {
        String[] list = TrashDB.getServerRequestWhiteList();
        for (String white : list) {
            if (white.equalsIgnoreCase(path)) {
                return true;
            }
        }
        return false;
    }

    private void matchTrash(boolean isLastDepth, int maxDepth, String dirPath, AllTrashBean trash,
                            String root, int currentDeep) {
        boolean isHit = false;
        if (trash == null) {
            notFoundSence(isLastDepth, maxDepth, dirPath, trash, root, currentDeep);
        } else {
            List<CacheTrashBean> cacheList = trash.cacheTrashBean;
            for (CacheTrashBean bean : cacheList) {
                if (isLastDepth) {
                    if (bean.filePath.startsWith(dirPath)) {
                        isHit = true;
                        if (new File(root + bean.filePath).exists()) {
                            onFoundTrash(bean, new File(root + bean.filePath), true);
                        }
                    }
                } else {
                    if (FileUtils.removeSlash(bean.filePath).equalsIgnoreCase(dirPath)) {
                        isHit = true;
                        onFoundTrash(bean, new File(root + dirPath), true);
                        break;
                    }
                }
            }
            List<ResidualTrashBean> residualList = trash.residualTrashBean;
            for (ResidualTrashBean bean : residualList) {
                if (isLastDepth) {
                    if (bean.filePath.startsWith(dirPath)) {
                        isHit = true;
                        if (new File(root + bean.filePath).exists()) {
                            onFoundTrash(bean, new File(root + bean.filePath), true);
                        }
                    }
                } else {
                    if (FileUtils.removeSlash(bean.filePath).equalsIgnoreCase(dirPath)) {
                        isHit = true;
                        onFoundTrash(bean, new File(root + dirPath), true);
                        break;
                    }
                }
            }
            if (!isHit) {
                notFoundSence(isLastDepth, maxDepth, dirPath, trash, root, currentDeep);
            }
        }
    }

    private void notFoundSence(boolean isLastDepth, int maxDepth, String dirPath,
                               AllTrashBean trash, String root, int currentDeep) {
        if (isLastDepth) {
            mUnknowPathList.add(dirPath.toLowerCase());
        } else {
            OptimizerFile file = new OptimizerFile(root + dirPath);// listFile的时候需要完整路径,例:(/sdcard/tencent/msg)
            File[] subFils = null;
            try {
                subFils = file.listFiles();
            } catch (Throwable e) {
            }
            if (subFils != null && subFils.length > 0) {
                for (File subFile : subFils) {
                    if (subFile.isDirectory()) {
                        // 递归的时候把完整路径进行截取成 全路径 例:(/tencent/msg)
                        String realSubPath = subFile.getAbsolutePath().substring(root.length()).toLowerCase();
                        listTraversal(currentDeep, maxDepth, realSubPath, trash, root);
                    }
                }
            }
        }
    }

    /**
     * 检出垃圾
     */
    private void onFoundTrash(Object trash, File file, boolean isNative) {
        TrashItem item = null;
        if (trash instanceof ResidualTrashBean) {
            ResidualTrashBean bean = (ResidualTrashBean) trash;
            if (isNative) {
                mNativeHitResidual.add(bean);
            }
            item = buildResidual(bean, file, isNative);
            if (item != null) {
                mTrashesData.putTrash(TrashType.UNINSTALLED_APP, item);
            }
        } else if (trash instanceof CacheTrashBean) {
            CacheTrashBean bean = (CacheTrashBean) trash;
            if (isNative) {
                mNativeHitCache.add(bean);
            }
            if (mDecodedPkgsMap.get(bean.pkgName) != null) {
                AppInfoCompat info = null;
                try {
                    info = AppManagerCompat.getAppInfo(mDecodedPkgsMap.get(bean.pkgName));
                } catch (Exception e) {
                }
                item = buildCache(bean, file, info, isNative);
                if (item != null) {
                    mTrashesData.putTrash(TrashType.APP_TRASH_FILE, item);
                }
            }
        }
    }

    private AppTrashItem buildResidual(ResidualTrashBean bean, File file, boolean isNative) {
        if (mDBFilter.needFilter(bean)) {
            return null;
        }
        AppTrashItem item = new AppTrashItem();
        item.pkgName = bean.pkgNames;
        item.trashType = TrashType.UNINSTALLED_APP;
        item.filePath = file.getAbsolutePath();
        item.cleanSuggest = bean.deleteLevel;
        item.isSelectedDefault = item.isSelected = item.cleanSuggest == AppTrashItem.CLEAN_SUGGEST_CLEAN;
        item.pathDesp = "";
        item.alertInfo = bean.alertInfo;
        item.isDir = file.isDirectory();
        item.appName = bean.softName;
        item.id = bean.fileId;
        long[] countAndSize = TrashUtils.getFileCountAndSizeWithDepth(file, TrashUtils.DEPTH_MAX);
        item.fileCount = (int) countAndSize[0];
        item.size = countAndSize[1];
        mResidualSize += item.size;
        if (FeatureConfig.DEBUG_LOG) {
            if (isNative) {
                LogHelper.i(SUB_TAG, "本地检出残留垃圾路径＝" + item.filePath + "   垃圾大小为:" + item.size);
            } else {
                LogHelper.i(SUB_TAG, "云端检出残留垃圾路径＝" + item.filePath + "   垃圾大小为:" + item.size);
            }
        }
        return item;
    }

    private AppTrashItem buildCache(CacheTrashBean bean, File file, AppInfoCompat info, boolean isNative) {
        AppTrashItem item = new AppTrashItem();
        item.appName = info == null ? "" : info.getLabel();
        item.trashType = TrashType.APP_TRASH_FILE;
        item.filePath = file.getAbsolutePath();
        item.pkgName = mDecodedPkgsMap.get(bean.pkgName);
        item.cleanSuggest = bean.deleteLevel;
        item.isSelectedDefault = item.isSelected = item.cleanSuggest == AppTrashItem.CLEAN_SUGGEST_CLEAN;
        item.pathDesp = bean.description;
        item.alertInfo = bean.alertInfo;
        item.contentType = bean.contentType;
        item.itemName = bean.itemName;
        item.isDir = file.isDirectory();
        item.id = bean.fileId;

        long[] countAndSize = TrashUtils.getFileCountAndSizeWithDepth(file, TrashUtils.DEPTH_MAX);
        item.fileCount = (int) countAndSize[0];
        item.size = countAndSize[1];
        mCacheFileSize += item.size;
        if (FeatureConfig.DEBUG_LOG) {
            if (isNative) {
                LogHelper.i(SUB_TAG, "本地检出缓存垃圾路径＝" + item.filePath + "   垃圾大小为:" + item.size);
            } else {
                LogHelper.i(SUB_TAG, "云端检出缓存垃圾路径＝" + item.filePath + "   垃圾大小为:" + item.size);
            }
        }
        return item;
    }

    private List<String> getRootPathList() {
        TraversalSDPathTask task = new TraversalSDPathTask();
        return task.traversalSD(mContext);
    }

    /**
     * @param dirs 一级路径集合
     */
    private Map<String, AllTrashBean> querySubDirs(List<String> dirs) {
        prepareWhiteListAndUnknowPath();
        mDecodedPathsMap = new HashMap<String, String>(dirs.size());

        //数据去斜杠去除，生成数据
        for (int i = 0; i < dirs.size(); i++) {
            String filePath = FileUtils.removeFrontSlash(dirs.get(i));
            String encodePathString = Decode.encode(filePath);
            dirs.set(i, encodePathString);
            mDecodedPathsMap.put(encodePathString, filePath);
        }

        TrashDB trashDB = TrashDB.getInstance();
        //获取包名加密对应关系，key为加密后的包名，value为包名
        List<String> installedAppsPkgNames = TrashUtils.getInstallPkgs();

        initEncodePkgs(installedAppsPkgNames);

        mDBFilter = new TrashDBBeanFilter(installedAppsPkgNames);
        // 查询数据库获取相应数据
        mAllRegulars = trashDB.getAllRegulars(installedAppsPkgNames, mQueryType);
        return trashDB.getTraversalAllTrash(dirs, mQueryType, mDecodedPathsMap);
    }

    //获取包名对应加密关系map
    private void initEncodePkgs(List<String> installedAppsPkgNames) {
        if (installedAppsPkgNames == null || installedAppsPkgNames.size() == 0) {
            return;
        }

        for (int i = 0, len = installedAppsPkgNames.size(); i < len; i++) {
            String pkgName = installedAppsPkgNames.get(i);
            String encodedPkgName = Decode.encode(pkgName.toLowerCase());
            installedAppsPkgNames.set(i, encodedPkgName);
            mDecodedPkgsMap.put(encodedPkgName, pkgName);
        }
    }

    private void prepareWhiteListAndUnknowPath() {
        TrashDB trashDB = TrashDB.getInstance();
        if (mSafeListInDB == null) {
            mSafeListInDB = trashDB.getWhitePath();
        }
        if (mUnknowListInDB == null) {
            mUnknowListInDB = trashDB.getUnknownPath();
        }
    }

    private boolean isInSafeList(String filePath) {
        if (mSafeListInDB == null || filePath == null) {
            return false;
        }

        for (FilePathBean whitePath : mSafeListInDB) {
            if (whitePath.path.equalsIgnoreCase(filePath)) {
                return true;
            }
        }
        return false;
    }

    private boolean isInUnknowList(String filePath) {
        if (mUnknowListInDB == null || filePath == null) {
            return false;
        }
        for (FilePathBean unKnowPath : mUnknowListInDB) {
            if (FileUtils.removeSlash(unKnowPath.path).equalsIgnoreCase(filePath)) {
                return true;
            }
        }
        return false;
    }

    /**
     * get the file size and count
     *
     * @param file is a directoryFile
     */
    private void getRegularTrash(File file, RegularTrashData rtd) {
        long[] countAndSize = TrashUtils.getFileCountAndSizeWithDepth(file, 3);//暂定改为3层
        rtd.count += countAndSize[0];
        rtd.size += countAndSize[1];
    }

    /**
     * 正则处理入口函数
     *
     * @param progress 进度信息
     */
    private void doRegularScan(int progress) {
        if (null == mUnknowPathList || mUnknowPathList.isEmpty()) {
            scanFinish(progress);
            return;
        }
        HashSet<String> findRegularSet = new HashSet<String>();
        for (String rootDir : mRootDirs) {
            mCacheFileSize += doRegularCacheTrashScan(rootDir, findRegularSet);
        }
        boolean bFilter = false;
        for (String unknowItem : mUnknowPathList) {
            bFilter = false;
            for (String regularItem : findRegularSet) {
                if (regularItem.startsWith(unknowItem)) {
                    bFilter = true;
                    if (FeatureConfig.DEBUG_LOG) {
                        LogHelper.i(SUB_TAG, "lyf正则过滤的未知路径＝" + unknowItem);
                    }
                    break;
                }
            }
            if (!bFilter) {
                mCloudQueryList.add(unknowItem);
            }
        }
        if (FeatureConfig.DEBUG_LOG) {
            LogHelper.i(SUB_TAG, "lyfmUnknowPathList size＝" + mUnknowPathList.size()
                    + ";mCloudQueryList size＝" + mCloudQueryList.size()
                    + ";findRegularSet size＝" + findRegularSet.size());
        }
        mUnknowPathList.clear();
        mTrashHandler.updateProgress(progress, null);
        mAllRegularTrashBean.clear();
    }

    /**
     * 正则匹配字符串
     *
     * @param strSrc  需要匹配的字符串
     * @param regular 正则表达式
     *
     * @return true：匹配成功；false：匹配失败
     */
    private boolean matchRegular(String strSrc, String regular) {
        Pattern subPathRegexPattern = null;
        try {
            subPathRegexPattern = Pattern.compile(regular);
        } catch (PatternSyntaxException e) {
            subPathRegexPattern = null;
        }
        if (null == subPathRegexPattern) {
            throw new IllegalArgumentException("z.m:\"" + regular + "\".");
        }

        Matcher matcher = subPathRegexPattern.matcher(strSrc);
        return null != matcher && matcher.matches();
    }

    /**
     * 目录名 匹配本级的所有正则式子
     *
     * @param strDirName   目录名
     * @param needMatchMap map的key需要匹配的本级正则式子，value是所对应完整正则式子list。
     *
     * @return 返回匹配的完整正则表达式的list。
     */
    private List<RegularTrashData> matchRegurarDirName(String strDirName,
                                                       Map<String, List<RegularTrashData>> needMatchMap) {
        List<RegularTrashData> regularList = new ArrayList<RegularTrashData>();
        for (Entry<String, List<RegularTrashData>> item : needMatchMap.entrySet()) {
            String key = item.getKey();
            List<RegularTrashData> value = item.getValue();
            if (matchRegular(strDirName, key)) {
                regularList.addAll(value);
            }
        }
        return regularList;
    }

    /**
     * 递归处理 文件所对应的正则list
     *
     * @param parentFile    处理的目录的文件对象
     * @param regularTrashs 正则数据list
     * @param deep          固定目录下递归的级数。0:表示再递归处理固定路径
     */
    private void matchRegularList(File parentFile, List<RegularTrashData> regularTrashs, int deep) {
        if (mStopRequested) {
            return;
        }
        if (FeatureConfig.DEBUG_LOG) {
            String strRegular = "";
            for (RegularTrashData regular : regularTrashs) {
                strRegular += (";" + regular.regularTrashBean.regulars);
            }
            LogHelper.v(SUB_TAG,
                    "regular_cache deal parentPath=" + parentFile.getAbsolutePath() + ";strRegular=" + strRegular);
        }
        //key 正则式子子目录 value 正则RegularTrashData list。
        Map<String, List<RegularTrashData>> needMatchMap = new HashMap<String, List<RegularTrashData>>();
        for (RegularTrashData regularItem : regularTrashs) {
            if (null == regularItem.regularArray) {
                //通过分隔符得到 正则数组。
                String strRegular = null;
                if (regularItem.regularTrashBean.regulars.startsWith("/")) {
                    strRegular = regularItem.regularTrashBean.regulars.substring(1);
                } else {
                    strRegular = regularItem.regularTrashBean.regulars;
                }
                regularItem.regularArray = strRegular.split("/");
            }
            if (regularItem.regularArray.length <= deep) {
                //检出出结果
                regularItem.trashFilePaths.add(parentFile.getAbsolutePath());
                getRegularTrash(parentFile, regularItem);
                if (FeatureConfig.DEBUG_LOG) {
                    LogHelper.v(SUB_TAG, "regular_cache find item:" + parentFile.getAbsolutePath()
                            + ";count=" + regularItem.count
                            + ";size=" + regularItem.size);
                }
                continue;
            }
            String strRegularItem = regularItem.regularArray[deep];
            List<RegularTrashData> regularList = null;
            if (needMatchMap.containsKey(strRegularItem)) {
                regularList = needMatchMap.get(strRegularItem);
            } else {
                regularList = new ArrayList<RegularTrashData>();
                needMatchMap.put(strRegularItem, regularList);
            }
            regularList.add(regularItem);
        }
        //得到子目录名称数组
        String[] allSubPath = TrashUtils.listDir(parentFile);
        if (null == allSubPath || 0 == allSubPath.length) {
            return;
        }
        //循环目录数组，得到目录满足的正则表达式。
        for (int i = 0; i < allSubPath.length; i++) {
            String subDirName = allSubPath[i];
            List<RegularTrashData> regularList = matchRegurarDirName(subDirName, needMatchMap);
            if (!regularList.isEmpty()) {
                matchRegularList(new File(parentFile, subDirName), regularList, deep + 1);
            }
        }
    }

    /**
     * 构建 正则垃圾信息
     *
     * @param rootFile 正则固定路径的对象
     * @param data     正则对象
     *
     * @return size大小
     */
    private long buildRegularResult(File rootFile, RegularTrashData data) {
        String pkgName = mDecodedPkgsMap.get(data.regularTrashBean.pkgName);
        AppInfoCompat info = null;
        try {
            info = AppManagerCompat.getAppInfo(pkgName);
        } catch (NameNotFoundException e) {
        }
        String appName = info == null ? "" : info.getLabel();
        AppTrashItem item = new AppTrashItem();
        item.appName = appName;
        item.trashType = TrashType.APP_TRASH_FILE;
        item.filePath = rootFile.getAbsolutePath();
        item.pkgName = pkgName;
        item.id = data.regularTrashBean.id;
        item.cleanSuggest = data.regularTrashBean.isDeleteDir;
        item.isSelectedDefault = item.isSelected = item.cleanSuggest == AppTrashItem.CLEAN_SUGGEST_CLEAN;
        item.alertInfo = data.regularTrashBean.alertInfo;
        item.contentType = data.regularTrashBean.contentType;
        item.itemName = data.regularTrashBean.itemName;
        item.isDir = true;
        item.size = data.size;
        item.fileCount = (int) data.count;
        item.isRegular = true;
        item.regularPaths = data.trashFilePaths;
        if (!mStopRequested) {
            mTrashesData.putTrash(TrashType.APP_TRASH_FILE, item);
            if (FeatureConfig.DEBUG_LOG) {
                LogHelper.v(SUB_TAG, "regular_cache result"
                        + " appName:" + item.appName
                        + " deleteLev:" + item.cleanSuggest
                        + " filePath:" + item.filePath
                        + " sign=" + TrashUtils.isUseJniFunction());
                for (String strItem : data.trashFilePaths) {
                    LogHelper.v(SUB_TAG, "regular_cache result item:" + strItem);
                }
            }
        }
        return item.size;
    }

    /**
     * 处理正则的入口函数
     *
     * @param curRootDir      根目录
     * @param findRegularList
     *
     * @return 正则垃圾总大小
     */
    private long doRegularCacheTrashScan(String curRootDir, Set<String> findRegularList) {
        long size = 0;
        HashMap<File, List<RegularTrashData>> allRegularTrashBean = new HashMap<File, List<RegularTrashData>>();
        File file = null;
        // 先将所有的正则表达式组织成一个路径(rootPath),对应多个正则表达式
        if (mAllRegulars != null) {
            for (RegularTrashBean rb : mAllRegulars) {
                if (mStopRequested) {
                    return size;
                }
                file = new File(curRootDir, rb.rootPath);
                if (file.exists()) {
                    List<RegularTrashData> list = allRegularTrashBean.get(file);
                    if (list == null) {
                        ArrayList<RegularTrashData> regularTrashDatas = new ArrayList<RegularTrashData>();
                        regularTrashDatas.add(new RegularTrashData(rb));
                        allRegularTrashBean.put(file, regularTrashDatas);
                    } else {
                        list.add(new RegularTrashData(rb));
                    }
                }
            }
        }
        // 对所有文件-正则表达式 开始遍历
        for (Entry<File, List<RegularTrashData>> en : allRegularTrashBean.entrySet()) {
            if (mStopRequested) {
                return size;
            }
            File rootFile = en.getKey();
            final List<RegularTrashData> regularTrashs = en.getValue();
            matchRegularList(rootFile, regularTrashs, 0);
            for (RegularTrashData data : regularTrashs) {
                if (mStopRequested) {
                    return size;
                }

                //获得正则所有匹配文件路径
                for (String strTrashPath : data.trashFilePaths) {
                    String strSubDir = strTrashPath.substring(curRootDir.toString().length()).toLowerCase();
                    findRegularList.add(strSubDir);
                    if (FeatureConfig.DEBUG_LOG) {
                        LogHelper.v(SUB_TAG, "lyf正则找到的垃圾路径:" + strSubDir);
                    }
                }

                if (data.size != 0 && data.count != 0) {
                    size += buildRegularResult(rootFile, data);
                }
            }
        }
        return size;
    }
}
